/**
 * Triangle.cpp
 *
 * Author:
 *   Chris Herbison
 *
 * Description:
 *   Represents a triangle of given coordinates and normal in world space.
 */

#include "Triangle.h"
#include <iostream>
#include <fstream>

const double PI = 3.14159265;

/*
 * The contructor.
 *
 * _normal - The normal vector of the object.
 * _verticies - The coordinates of each point of the object.
 * _color - The color of the object.
 */
Triangle::Triangle(IlluminationData _material, TextureData _texture,
                 Vector3 _normal, vector<Point3> _verticies): 
                 Object(_material, _texture), normal(_normal), verticies(_verticies){
     normal.normalize();
}

/*
 * Checks if a given ray intersects with this object.
 *
 * r - The ray to check for intersection.
 * data - Stores all needed data from the intersection.
 *
 * return (bool) - Whether or not the ray intersects with this polygon.
 */
bool Triangle::intersects(Ray r, IntersectData* data/*, ofstream* outf*/){
     Vector3 D = r.direction;
     D.normalize();
     //Load up the verticies of the triangle.
     Point3 t1 = verticies.at(0);
     Point3 t2 = verticies.at(1);
     Point3 t3 = verticies.at(2);
     double denominator = normal*D;
     //If ray and plane are parallel, no intersect.
     if(denominator == 0){
          return false;
     }
     //Calculate F from plane equation.
     double F = -normal.x*t1.x-normal.y*t1.y-normal.z*t1.z;
     
     Vector3 temp(r.origin.x, r.origin.y, r.origin.z);
     double t = -((normal*temp)+F)/denominator;
     //If plane is behind ray's origin, no intersect.
     if(t<0){
          return false;
     }
     
     //Calculate the point on the plane the intersection occurs at.
     double pX = r.origin.x+(t*D.x);
     double pY = r.origin.y+(t*D.y);
     double pZ = r.origin.z+(t*D.z);
     
     Vector3 point(pX,pY,pZ);
     
     //Draw a vector from the intersection point to each point on the triangle.
     Vector3 Pt1(point.x-t1.x, point.y-t1.y, point.z-t1.z);
     Vector3 Pt2(point.x-t2.x, point.y-t2.y, point.z-t2.z);
     Vector3 Pt3(point.x-t3.x, point.y-t3.y, point.z-t3.z);
     
     //Get the angle between these lines.
     double theta0 = acos((Pt1*Pt2)/(Pt1.length()*Pt2.length())) * 180/PI;
     double theta1 = acos((Pt2*Pt3)/(Pt2.length()*Pt3.length())) * 180/PI;
     double theta2 = acos((Pt3*Pt1)/(Pt3.length()*Pt1.length())) * 180/PI;

     (*data).pos.x = pX;
     (*data).pos.y = pY;
     (*data).pos.z = pZ;
     (*data).normal = normal;
     
     double theta = theta0 + theta1 + theta2;
     //If all angles = 360, point is inside triangle.
     if(((int)theta) == 360){
          //Add all relevant data to the IntersectionData object.
          vector<MyColor> colors(texture.getColors((*data).pos));
          (*data).CA = colors.at(0);
          (*data).CD = colors.at(1);
          (*data).CS = colors.at(2);
          Vector3 incoming((*data).pos - r.origin);
          incoming.normalize();
          normal.normalize();
          Vector3 tempArg((2*(incoming*normal))*normal);
          Vector3 reflect(incoming-tempArg);
          reflect.normalize();
          (*data).reflective = reflect;
          (*data).view = incoming;
          
          //If object is transmissive, calculate transmission ray.
          if(material.kt > 0){
               double nit;
               Vector3 theNormal = (*data).normal;
               if(parity){
                    nit = material.n/1;
                    theNormal*= -1;
               }
               else{
                    nit = 1/material.n;
               }
               double cosTheta = ((-1)*incoming)*theNormal;
               double sqrtTerm = 1+((nit*nit)*(((cosTheta)*(cosTheta))-1));
               //Check for total internal reflection.
               if(sqrtTerm < 0){
                    (*data).transmissive = (*data).reflective;
               }
               else{
                    (*data).transmissive = (nit*incoming)+(((nit)*cosTheta)-(sqrt(sqrtTerm)))*theNormal;
               }
          }
          
          return true;
     }
     else{
          return false;
     }
}

//Function's implementation is inaccurate, does not cover the case in which no verticies lie inside the box but the triangle intersects the box.
bool Triangle::intersects(BoundingBox b){
	bool result = false;

	float minX = (float)b.minValues.x;
	float maxX = (float)b.maxValues.x;
	float minY = (float)b.minValues.y;
	float maxY = (float)b.maxValues.y;
	float minZ = (float)b.minValues.z;
	float maxZ = (float)b.maxValues.z;

	if(verticies.at(0).x <= maxX && verticies.at(0).x >= minX &&
		verticies.at(0).y <= maxY && verticies.at(0).y >= minY &&
		verticies.at(0).z <= maxZ && verticies.at(0).z >= minZ){
		result = true;
	}
	else if(verticies.at(1).x <= maxX && verticies.at(1).x >= minX &&
		verticies.at(1).y <= maxY && verticies.at(1).y >= minY &&
		verticies.at(1).z <= maxZ && verticies.at(1).z >= minZ){
		result = true;
	}
	else if(verticies.at(2).x <= maxX && verticies.at(2).x >= minX &&
		verticies.at(2).y <= maxY && verticies.at(2).y >= minY &&
		verticies.at(2).z <= maxZ && verticies.at(2).z >= minZ){
		result = true;
	}

	return result;
}

BoundingBox Triangle::getBoundingBox(){
	//return BoundingBox.getBoundsOn( verticies );
	BoundingBox b;
	return b;
}
